﻿
using System.Reactive.Linq;

using TimeSpan.Services;

namespace TimeSpan.Models;

public class SpanTaskOption {

    public string Title { get; set; } = string.Empty;

    // 每个时间块的专注时间 
    public int BlockDuration { get; set; }

    // 总时长
    public int TotalDuration { get; set; }

}

public class SpanBlock {

    public string Title { get => Type switch {
        SpanBlockType.Work => "Span Time",
        SpanBlockType.Rest => "Interval Rest",
        SpanBlockType.LongRest => "Long Rest",
        SpanBlockType.Last => "Review Item",
        _ => throw new NotSupportedException()
    }; }

    // 时间块顺序
    public required int Index { get; init; }
    
    // 时间块时间
    public required int Duration { get; set; }     // 单位 分

    // 时间块类型
    public required SpanBlockType Type { get; set; }         

}

public enum SpanBlockType {
    Work,
    Rest,
    LongRest,
    Last,
}

public partial class SpanTask : ObservableObject {

    private readonly SpanTaskOption option;

    public SpanTask(SpanTaskOption option) {
        Title = option.Title;
        this.option = option;
        Init(option);
    }

    private void Init(SpanTaskOption option) {
        int totalDuration = option.TotalDuration;
        int blockDuration = option.BlockDuration;

        int index = 0;

        while (totalDuration > blockDuration) {
            Blocks.Add(new SpanBlock { Index = ++index, Duration = blockDuration, Type = SpanBlockType.Work });
            totalDuration -= blockDuration;
            var is3times = (Blocks.Count(x => x.Type == SpanBlockType.Work) % 3 == 0);
            var restDuration = is3times ? 15 : 5;
            var type = is3times ? SpanBlockType.LongRest : SpanBlockType.Rest;
            if (totalDuration > restDuration) {
                Blocks.Add(new SpanBlock { Index = ++index, Duration = restDuration, Type = type });
                totalDuration -= restDuration;
            }
        }

        if (totalDuration != 0) {
            Blocks.Add(new() { Index = ++index, Duration = totalDuration, Type = SpanBlockType.Last });
        }
    }

    public string Title { get; }

    public List<SpanBlock> Blocks { get; } = [];

    #region 以下是运行信息

    private IDisposable? task;

    private bool isRunning = false; 

    public void Start() {
        if (isRunning) { return; }
        task ??= Observable.Interval(System.TimeSpan.FromMilliseconds(100)).Where(x => isRunning).Do(_ => Tick()).Subscribe();
        var currentBlockLeft = CurrentBlockMinutesLeft * 60 + CurrentBlockSecondsLeft; // 秒数
        CurrentBlockFinishTime = DateTime.Now + System.TimeSpan.FromSeconds(currentBlockLeft);
        isRunning = true;
    }

    public void Pause() {
        if (!isRunning) { return; }
        isRunning = false;
    }

    public void Tick() {
        if (DateTime.Now <= CurrentBlockFinishTime) {
            var totalLeft = CurrentBlockFinishTime - DateTime.Now;
            CurrentBlockMinutesLeft = totalLeft.Minutes;
            CurrentBlockSecondsLeft = totalLeft.Seconds;
        } else {
            ToNextBlockAndWaitForStart();
        }
    }

    public void Settle() {
        Pause();
        IsFinish = true;
        task?.Dispose();
        FinishBlocksTotalMinutes = FinishBlocksTotalSeconds / 60;
    }

    public void Skip() {
        ToNextBlockAndWaitForStart();
    }

    private void CalcCurrentBlock() {
        if (CurrentBlock is null) { return; }
        if (CurrentBlock.Type == SpanBlockType.Work) { FinishedWorkTimes++; }
        if (CurrentBlock.Type == SpanBlockType.Rest) { FinishedRestTimes++; }
        if (CurrentBlock.Type == SpanBlockType.LongRest) { FinishedLongRestTimes++; }
        if (CurrentBlock.Type == SpanBlockType.Last) { 
            FinishedReviewTimes++; 
            ReviewMinutes = CurrentBlockTotalDuration - CurrentBlockMinutesLeft;
        }

        FinishBlocksTotalSeconds += CurrentBlockTotalDuration * 60 - CurrentBlockMinutesLeft * 60 - CurrentBlockSecondsLeft;
    }

    private void ToNextBlockAndWaitForStart() {
        Pause();

        CalcCurrentBlock();

        if (runningBlocks.Count == 0) {
            Settle();
            return;
        }

        CurrentBlock = runningBlocks.Dequeue();
        CurrentBlockTitle = CurrentBlock.Title;
        CurrentBlockTotalDuration = CurrentBlock.Duration;
        CurrentBlockMinutesLeft = CurrentBlock.Duration;
        CurrentBlockSecondsLeft = 0;
    }

    public void PrepareToStart() {
        runningBlocks = new();
        foreach (var block in Blocks) {
            runningBlocks.Enqueue(block);
        }

        ToNextBlockAndWaitForStart();
    }

    public bool IsFinish = false;

    private Queue<SpanBlock> runningBlocks = [];

    [ObservableProperty]
    private SpanBlock? currentBlock;

    [ObservableProperty]
    private string currentBlockTitle = string.Empty;

    [ObservableProperty]
    private int currentBlockTotalDuration = 0;

    [ObservableProperty]
    private int currentBlockMinutesLeft = 0;
    
    [ObservableProperty]
    private int currentBlockSecondsLeft = 0;
    
    [ObservableProperty]
    private DateTime currentBlockFinishTime = DateTime.Now;

    [ObservableProperty]
    private int finishBlocksTotalSeconds = 0;

    [ObservableProperty]
    private int finishedWorkTimes = 0;

    [ObservableProperty]
    private int finishedRestTimes = 0;

    [ObservableProperty]
    private int finishedLongRestTimes = 0;

    [ObservableProperty]
    private int finishedReviewTimes = 0;
    
    [ObservableProperty]
    private int reviewMinutes = 0;

    [ObservableProperty]
    private int finishBlocksTotalMinutes = 0;

    #endregion


    #region 以下是基础信息

    public int TotalDuration { get => option.TotalDuration; }
    
    public int WorkTimes { get => Blocks.Count(x => x.Type == SpanBlockType.Work); }

    public int RestTimes { get => Blocks.Count(x => x.Type == SpanBlockType.Rest); }

    public int LongRestTimes { get => Blocks.Count(x => x.Type == SpanBlockType.LongRest); }

    public int ReviewItem { get => Blocks.SingleOrDefault(x => x.Type == SpanBlockType.Last)?.Duration ?? 0; }
    
    public int ReviewTimes { get => Blocks.Count(x => x.Type == SpanBlockType.Last); }

    public int SpanDuration { get => option.BlockDuration; }

    #endregion
}